package plgutils

import (
	"dgo/goutils/utils"
	"fmt"
	"gitee.com/ymofen/supervalue"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

/*
*
 */
type ObjStoragePojo struct {
	lk           sync.RWMutex
	Owner        *ObjStoragePlg
	Sn           int                 // 排序
	Id           string              // 对象ID
	Num          int                 // 总数量
	UsageNum     int                 // 使用数量
	MatchPartial string              // 部分匹配
	Data         *wrapper.SuperValue // 数据属性
	DataStr      string              // 数据属性字符串

	// 访问的Session
	SessionMap map[string]*ObjStorageSession

	RequestData    string
	ReleaseReason  string
	BlockT         time.Time
	ObjectGetT     time.Time
	ObjectGetTimes int // 总使用计数

	RequestUserlst *utils.RingStrlist // 请求的用户列表

	State int8 // 0/-1:不可用

	loadflag int8
}

/*
*

	重载数据

	{
		"id":"0zyn004",	"num":1, "data":{}, "state":1, "matchpartial":"[][]"
	}
*/
func (this *ObjStoragePojo) ReloadData(json *wrapper.SuperValue) {
	this.lk.Lock()
	defer this.lk.Unlock()
	this.Id = json.StringByName("id", "") // 记录id
	this.State = json.Int8ByName("state", 1)
	this.MatchPartial = json.StringByName("matchpartial", "")
	this.Num = json.IntByName("num", 1)
	data := json.ValueByName("data")
	if data.IsObject() {
		this.Data = data.Clone()
		this.DataStr = data.String()
	} else {
		if data != nil {
			this.DataStr = data.String()
		} else {
			this.DataStr = ""
		}

		if len(this.DataStr) > 0 {
			this.Data, _ = wrapper.NewSVFromBuf([]byte(this.DataStr))
		} else {
			this.Data = nil
		}
	}
}

func (this *ObjStoragePojo) GetBlockT() time.Time {
	if this.BlockT.IsZero() || this.BlockT.Before(time.Now()) {
		return utils.ZeroTime
	} else {
		return this.BlockT
	}

}

func (this *ObjStoragePojo) GetObjectData() (json *wrapper.SuperValue) {
	data := this.Data
	if data == nil {
		return nil
	}
	return data.Clone()
}

func (this *ObjStoragePlg) checkOffline() {
	this.objectlst.Range(func(key, value interface{}) bool {
		itm := value.(*ObjStoragePojo)
		if itm.UsageNum > 0 {
			itm.lk.RLock()
			for k, v := range itm.SessionMap {
				if time.Since(v.LastTime).Seconds() > 60 {
					sessionid := k
					id := itm.Id
					utils.DefaultWorkers().PostTaskFunc(this.PlugId, func() {
						this.ObjectRelease(sessionid, id, "超时未收到PING", -1)
					})
				}
			}
			itm.lk.RUnlock()
		}
		return true
	})
}

func (this *ObjStoragePlg) checkGetPojo(id string, newflag bool) *ObjStoragePojo {
	if !newflag {
		return this.objectlst.Get(id, (*ObjStoragePojo)(nil)).(*ObjStoragePojo)
	}
	rval := this.objectlst.LoadOrStoreFunc(id, func() interface{} {
		rval := &ObjStoragePojo{Id: id, Owner: this, SessionMap: make(map[string]*ObjStorageSession), RequestUserlst: utils.NewRingStrlist(20)}
		return rval
	}, nil).(*ObjStoragePojo)
	return rval
}

/*
*

	指定对象获取
*/
func (this *ObjStoragePlg) ObjectGet(sess *ObjStorageSession, task *debugstepsession) (rval *ObjStoragePojo, err error) {
	pojo := this.checkGetPojo(sess.ObjectID, false)
	if pojo == nil {
		return nil, fmt.Errorf("获取失败:对象不存在")
	}

	task.debugstep = fmt.Sprintf("trylock：%s", pojo.Id)
	if this.TryLock(pojo, sess) {
		return pojo, nil
	} else {
		return pojo, fmt.Errorf("获取失败:对象")
	}
}

/*
根据策略获取
*/
func (this *ObjStoragePlg) ObjectGet2(sess *ObjStorageSession, task *debugstepsession) (rval *ObjStoragePojo, err error) {
	now := time.Now()
	this.objectlst.ListRangeV2(func(key, value interface{}) bool {
		pojo := value.(*ObjStoragePojo)
		ok := true

		if pojo.State != 1 {
			return false
		}
		if !pojo.BlockT.IsZero() && pojo.BlockT.After(now) { // 阻塞的帐号
			return false
		}

		if pojo.Num > 0 && pojo.UsageNum >= pojo.Num {
			return false
		}

		if len(pojo.MatchPartial) > 0 && len(sess.Match) > 0 {
			if !strings.Contains(pojo.MatchPartial, sess.Match) {
				return false
			}
		}

		return ok
	}, func(k0, v0, k1, v1 interface{}) bool {
		pojo0 := v0.(*ObjStoragePojo)
		pojo1 := v1.(*ObjStoragePojo)

		if this.matchCycle == 0 {
			if len(sess.RequestUser) > 0 { // 最后访问过的
				idx0 := pojo0.RequestUserlst.IndexLastWrite(sess.RequestUser)
				idx1 := pojo0.RequestUserlst.IndexLastWrite(sess.RequestUser)
				if idx0 != idx1 {
					return idx0 < idx1
				}
			}
		}

		if len(sess.Match) > 0 { // 优先选匹配区域的
			b0 := len(pojo0.MatchPartial) > 0
			b1 := len(pojo1.MatchPartial) > 0
			if b0 != b1 {
				return b0
			}
		}

		if pojo0.Sn != pojo1.Sn {
			return pojo0.Sn < pojo1.Sn
		}
		unx0 := pojo0.ObjectGetT.Unix()
		unx1 := pojo1.ObjectGetT.Unix()
		if unx0 != unx1 {
			return unx0 < unx1 // 刚使用过的排后面
		}
		return pojo0.Id < pojo1.Id
	}, func(key, val interface{}) bool {
		pojo := val.(*ObjStoragePojo)
		task.debugstep = fmt.Sprintf("TryLock:%s", pojo.Id)
		if this.TryLock(pojo, sess) {
			sess.ObjectID = pojo.Id
			rval = pojo
		}
		return rval == nil
	})
	if rval == nil {
		err = fmt.Errorf("没有可用账号")
	}
	return
}

/*
*

	已经被T掉
*/
func (this *ObjStoragePlg) onSessionKickOut(usr *ObjStoragePojo, sess, newsess *ObjStorageSession, reason string) {
	//this.LogWarnMessage("[%s][%s][%s]被挤使用时长:%s, 新的连接[%s][%s]进入, logindata:%s", usr.User, sess.SessionId, sess.FromIp, utils.HumanDuration(time.Since(sess.StartTime)),
	//	newsess.SessionId, newsess.RequestData)
	// 进行通知
	this.notifyEx.SendTaskTopic(func(json *wrapper.SuperValue) {
		json.SetKeyValues("action", "kickout", "sessionid", sess.SessionId,
			"clientid", sess.ObjectID, "ts", time.Now().Unix(), "reason", fmt.Sprintf("%s", reason))
	})
}

///**
//  需要对用户进行加锁
//*/
//func (this *ObjStoragePojo) findEarlySession() (session *ObjStorageSession) {
//	session = nil
//	for _, itm := range this.SessionMap {
//		if session == nil {
//			session = itm
//		} else if itm.StartTime.Before(session.StartTime) {
//			session = itm
//		}
//	}
//	return session
//}

/*
释放账号
*/
func (this *ObjStoragePlg) ObjectRelease(sessionid string, objectid, reason string, blocksecs int64) {
	pojo := this.checkGetPojo(objectid, false)
	if pojo == nil { // 未找到账号
		// 异常信息
		this.LogWarnMessage("未查询到指定对象[%s]信息", objectid)
		return
	}
	pojo.lk.Lock()
	defer pojo.lk.Unlock()

	this.sesslst.Remove(sessionid)

	// 只针对正在使用的账号
	sess := pojo.SessionMap[sessionid]
	if sess != nil {
		if len(sess.RequestUser) > 0 {
			pojo.RequestUserlst.CheckWrite(sess.RequestUser)
		}
		if blocksecs == 0 {
			pojo.BlockT = utils.ZeroTime
		} else if blocksecs > 0 {
			pojo.BlockT = time.Now().Add(time.Duration(blocksecs) * time.Second)
		}

		pojo.ReleaseReason = fmt.Sprintf("%s:%s", utils.NowString(), reason) // 记录释放信息
		delete(pojo.SessionMap, sessionid)
		pojo.UsageNum--
		if pojo.UsageNum < 0 {
			panic(fmt.Sprintf("UsageNum:%d", pojo.UsageNum))
		}
		r := atomic.AddInt32(&this.onlinecnt, -1)
		if r < 0 {
			panic(fmt.Sprintf("online:%d", this.onlinecnt))
		}
		this.LogInfoMessage("[%s][%s]释放对象, reason:%s", sessionid, pojo.Id, reason)
		return
	}
	// 异常信息
	this.LogWarnMessage("释放异常, err: %s不在对象Session列表%s中, reason:%s", sessionid, pojo.Id, reason)
	return
}

func (this *ObjStoragePlg) TryLock(pojo *ObjStoragePojo, sess *ObjStorageSession) bool {
	pojo.lk.Lock()
	defer pojo.lk.Unlock()
	if pojo.State != 1 {
		return false
	}
	if pojo.Num == 0 || pojo.UsageNum < pojo.Num {
		pojo.UsageNum++
		pojo.ObjectGetTimes++
		pojo.ObjectGetT = time.Now()
		pojo.SessionMap[sess.SessionId] = sess
		pojo.RequestData = sess.RequestData
		if len(sess.RequestUser) > 0 {
			pojo.RequestUserlst.CheckWrite(sess.RequestUser)
		}
		atomic.AddInt32(&this.onlinecnt, 1)
		this.sesslst.Set(sess.SessionId, sess)
		return true
	}
	return false
}

/*
*

	心跳PING(10s 一次)
	  1: 如果帐号不存在， 返回异常
*/
func (this *ObjStoragePlg) ObjectPing(sessionid string, Id string) (rval *ObjStorageSession, err error) {
	nowt := time.Now()
	if this.startuptime.IsZero() { // 初始化内存
		return nil, nil
	}

	pojo := this.checkGetPojo(Id, false)
	if pojo == nil { // 未找到账号
		return nil, fmt.Errorf("对象不存在, %s", Id)
	}

	if pojo.State != 1 {
		return nil, fmt.Errorf("对象不可用, %s", Id)
	}

	pojo.lk.Lock()
	defer pojo.lk.Unlock()

	sess := pojo.SessionMap[sessionid]
	if sess == nil {
		if nowt.Sub(this.startuptime).Seconds() < 60 { // 重启60s内  通过心跳，校准当前在线数
			sess = this.newSession(sessionid)
			sess.ObjectID = Id
			sess.StartTime = time.Now()
			sess.LastTime = time.Now()
			if this.TryLock(pojo, sess) {
				return sess, nil
			}
		}
		return nil, fmt.Errorf("SESSION已经下线, %s:%s", Id, sessionid)
	}
	sess.LastTime = time.Now()
	return sess, nil
}
